/**
 *                            License
 * THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS  
 * CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). 
 * THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW.  
 * ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR  
 * COPYRIGHT LAW IS PROHIBITED.
 * 
 * BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND  
 * AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE  
 * MAY BE CONSIDERED TO BE A CONTRACT,
 * THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED 
 * HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
 * 
 */
package lineage.core.thread.pool;

import java.util.TimerTask;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 线程池.
 * 
 * @version 2014年3月6日下午12:21:15
 * @author jrwz
 */
public final class L1ThreadPool {
    private static final Logger LOG = LoggerFactory.getLogger(L1ThreadPool.class);
    /** 池中所保存的线程数,包括空闲线程. */
    private static final int SCHEDULED_CORE_POOL_SIZE = 100;
    /** 执行已提交的 Runnable 任务的对象. */
    private final Executor executor;
    /** 一个 ExecutorService,可安排在给定的延迟后运行或定期执行的命令. */
    private final ScheduledExecutorService scheduler;

    /**
     * 静态初始化器，由JVM来保证线程安全.
     */
    private static class Holder {
        static L1ThreadPool instance = new L1ThreadPool();
    }

    /**
     * 取得该类的实例.
     */
    public static L1ThreadPool getInstance() {
        return Holder.instance;
    }

    /**
     * 线程池.
     */
    private L1ThreadPool() {
        executor = Executors.newCachedThreadPool(); // 创建一个可根据需要创建新线程的线程池，但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言，这些线程池通常可提高程序性能。
        // 创建一个线程池，它可安排在给定延迟后运行命令或者定期地执行。
        scheduler = Executors.newScheduledThreadPool(SCHEDULED_CORE_POOL_SIZE, new NPriorityThreadFactory("GSTPool", Thread.NORM_PRIORITY));
    }

    /**
     * 使该线程开始执行；Java 虚拟机调用该线程的 run 方法。
     * 结果是两个线程并发地运行；当前线程（从调用返回给 start 方法）和另一个线程（执行其 run 方法）。
     * 多次启动一个线程是非法的。特别是当线程已经结束执行后，不能再重新启动。
     * 
     * @param r
     *            - 其 run 方法被调用的对象
     */
    public void execute(final Runnable r) {
        try {
            if (executor == null) {
                final Thread t = new Thread(r); // 分配新的 Thread 对象
                t.start();
            } else {
                executor.execute(r); // 在未来某个时间执行给定的命令。该命令可能在新的线程、已入池的线程或者正调用的线程中执行，这由 Executor 实现决定。
            }
        } catch (final Exception e) {
            LOG.error(e.getLocalizedMessage(), e);
        }
    }

    /**
     * 创建并执行在给定延迟后启用的一次性操作.
     * 
     * @param r
     *            - 要执行的任务
     * @param delay
     *            - 从现在开始延迟执行的时间(单位：毫秒)
     * @return 表示挂起任务完成的 Future,并且其 get() 方法在完成后将返回 null
     */
    public ScheduledFuture<?> schedule(final Runnable r, final long delay) {
        try {
            if (delay <= 0) {
                executor.execute(r);
                return null;
            }
            return scheduler.schedule(r, delay, TimeUnit.MILLISECONDS);
        } catch (final RejectedExecutionException e) {
            LOG.error(e.getLocalizedMessage(), e);
        }
        return null;
    }

    /**
     * 创建并执行一个在给定初始延迟后首次启用的定期操作，后续操作具有给定的周期；
     * 也就是将在 <tt>initialDelay</tt> 后开始执行，然后在 <tt>initialDelay+period</tt> 后执行，接着在 <tt>initialDelay + 2 * period</tt> 后执行，依此类推。
     * 如果任务的任一执行遇到异常，都会取消后续执行。否则，只能通过执行程序的取消或终止方法来终止该任务.
     * 
     * @param r
     *            - 要执行的任务
     * @param initialDelay
     *            - 首次执行的延迟时间(单位：毫秒)
     * @param period
     *            - 连续执行之间的周期(单位：毫秒)
     * @return 表示挂起任务完成的 Future,并且其 get() 方法在取消后将抛出异常
     */
    public ScheduledFuture<?> scheduleAtFixedRate(final Runnable r, final long initialDelay, final long period) {
        try {
            return scheduler.scheduleAtFixedRate(r, initialDelay, period, TimeUnit.MILLISECONDS);
        } catch (final Exception e) {
            LOG.error(e.getLocalizedMessage(), e);
        }
        return null;
    }

    /**
     * 取消此计时器任务.
     * 如果任务安排为一次执行且还未运行，或者尚未安排，则永远不会运行。如果任务安排为重复执行，则永远不会再运行。（如果发生此调用时任务正在运行，则任务将运行完，但永远不会再运行。）
     * 注意，从重复的计时器任务的 run 方法中调用此方法绝对保证计时器任务永远不会再运行。
     * 此方法可以反复调用；第二次和以后的调用无效.
     * 
     * @param task
     *            - 要取消的任务
     */
    public void cancel(final TimerTask task) {
        try {
            task.cancel(); // 取消此计时器任务
        } catch (final Exception e) {
            LOG.error(e.getLocalizedMessage(), e);
        }
    }

    /**
     * 试图取消对此任务的执行.
     * 如果任务已完成、或已取消，或者由于某些其他原因而无法取消，则此尝试将失败.
     * 当调用 cancel 时，如果调用成功，而此任务尚未启动，则此任务将永不运行.
     * 如果任务已经启动，则 mayInterruptIfRunning 参数确定是否应该以试图停止任务的方式来中断执行此任务的线程.
     * 
     * @param future
     *            - 一个延迟的、结果可接受的操作，可将其取消
     * @param mayInterruptIfRunning
     *            - 如果应该中断执行此任务的线程，则为 true；否则允许正在运行的任务运行完成
     */
    public void cancel(final ScheduledFuture<?> future, boolean mayInterruptIfRunning) {
        try {
            future.cancel(mayInterruptIfRunning);
        } catch (final RejectedExecutionException e) {
            LOG.error(e.getLocalizedMessage(), e);
        }
    }

    /**
     * 内部类：优先级线程工厂.
     */
    private class NPriorityThreadFactory implements ThreadFactory {
        private final int prio;
        private final String name;
        /** 线程群组. */
        private final ThreadGroup group;
        /** 线程数量. */
        private final AtomicInteger threadNumber = new AtomicInteger(1);

        /**
         * 优先级线程工厂.
         * 
         * @param name
         *            - 线程名
         * @param prio
         *            - 优先级
         */
        public NPriorityThreadFactory(final String name, final int prio) {
            this.prio = prio;
            this.name = name;
            group = new ThreadGroup(this.name);
        }

        /**
         * 构造一个新 Thread.实现也可能初始化属性、名称、守护程序状态、ThreadGroup 等等.
         * 
         * @param r
         *            - 由新线程实例所执行的可运行线程
         * @return 构造的线程
         */
        @Override
        public Thread newThread(final Runnable r) {
            final StringBuilder sb = new StringBuilder();
            final Thread t = new Thread(group, r);
            sb.append(name);
            sb.append("-");
            sb.append(threadNumber.getAndIncrement()); // 以原子方式将当前值加 1
            t.setName(sb.toString());
            t.setPriority(prio); // 更改线程的优先级
            return t;
        }
    }
}
